Module modules.db
File name: db.py Author: Martin Jůda Python Version: 3.7 Description: Module with database connector and ORM mapper
Expand source code
"""
File name: db.py
Author: Martin Jůda
Python Version: 3.7
Description: Module with database connector and ORM mapper
"""
from datetime import datetime
import random
from sqlalchemy.pool import NullPool
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import (
create_engine,
Column,
Integer,
String,
DateTime,
LargeBinary,
ForeignKey,
Table,
)
from sqlalchemy.orm import sessionmaker, relationship
from sqlalchemy.sql.expression import func
from modules.constants import (
DB_CONNECTION_STRING,
DEFAULT_SUBJECT_NAME,
)
Base = declarative_base()
class SubjectEntity(Base):
"""Subject database table definition"""
__tablename__ = "subject"
department_shortcut = Column(String, primary_key=True)
name = Column(String(30))
# 1 (subject): M(questions) relationship
questions = relationship("QuestionEntity", uselist=True, back_populates="subject")
class QuestionEntity(Base):
"""Question database table definition"""
__tablename__ = "question"
id = Column(Integer, primary_key=True)
question = Column(String(255), nullable=False)
department_shortcut = Column(String, ForeignKey("subject.department_shortcut"))
right_answer = Column(String(255), nullable=False)
wrong_answer_1 = Column(String(255), nullable=False)
wrong_answer_2 = Column(String(255), nullable=False)
wrong_answer_3 = Column(String(255), nullable=False)
picture = Column(LargeBinary)
subject = relationship("SubjectEntity", uselist=False, back_populates="questions")
association_table = Table(
"device_permission",
Base.metadata,
Column("id", Integer, primary_key=True),
Column("device_id", Integer, ForeignKey("device.id")),
Column("study_programme_id", Integer, ForeignKey("study_programme.id")),
)
class DeviceEntity(Base):
"""Device database table definition"""
__tablename__ = "device"
id = Column(Integer, primary_key=True)
description = Column(String)
logs = relationship("LogEntity", uselist=True, back_populates="device")
study_programmes = relationship(
"StudyProgrammeEntity", secondary=association_table, back_populates="devices"
)
class LogEntity(Base):
"""Log database table definition"""
__tablename__ = "log"
id = Column(Integer, primary_key=True)
datetime = Column(DateTime, nullable=False)
device_id = Column(Integer, ForeignKey("device.id"), nullable=False)
card = Column(String, nullable=False)
device = relationship("DeviceEntity", uselist=False, back_populates="logs")
class StudyProgrammeEntity(Base):
"""Study programme table definition"""
__tablename__ = "study_programme"
id = Column(Integer, primary_key=True)
study_code = Column(String, nullable=False)
description = Column(String)
devices = relationship(
"DeviceEntity", secondary=association_table, back_populates="study_programmes"
)
class Database:
"""Database connection and SQLAlchemy wrapper"""
def __init__(self, db_url=None):
if db_url is None:
db_url = DB_CONNECTION_STRING
self.engine = create_engine(db_url, poolclass=NullPool)
self._create_tables()
def _create_tables(self):
Base.metadata.create_all(self.engine)
def get_db_session(self):
"""Create database connection
Returns:
Database session, represents SQL transaction
"""
session_maker = sessionmaker(bind=self.engine)
return session_maker()
@staticmethod
def select_questions(db_session, subjects, requested_number_of_questions):
"""Select questions for chose subjects
Args:
db_session: Database connection
subjects (list): list with subjects
requested_number_of_questions (int): number of requested questions
Returns:
list(QuestionEntity): questions
"""
questions = []
for subject in subjects:
# select subject entity
subject_entity = (
db_session.query(SubjectEntity)
.filter(SubjectEntity.department_shortcut == subject["subject_code"])
.first()
)
if subject_entity:
number_of_questions = len(subject_entity.questions)
if number_of_questions > 0:
# select random question for subject
index = random.choice(range(number_of_questions))
questions.append(subject_entity.questions[index])
questions_count = len(questions)
if questions_count < requested_number_of_questions:
# select default questions if there is less questions for chose subjects
default_questions = (
db_session.query(QuestionEntity)
.filter(QuestionEntity.department_shortcut == DEFAULT_SUBJECT_NAME)
.order_by(func.random())
.limit(requested_number_of_questions - questions_count)
)
questions.extend(default_questions)
return questions
@staticmethod
def find_question_image(db_session, file_id):
"""Select picture by ID
Args:
db_session: Database connection
file_id (int): ID of question
Returns:
QuestionEntity: entity with picture
"""
file = (
db_session.query(QuestionEntity.picture)
.filter(QuestionEntity.id == file_id)
.first()
)
if not file:
raise LookupError()
return file
@staticmethod
def device_by_id(db_session, device_id):
"""Check if device with given id exists in DB
Args:
db_session: Database connection
device_id(int): device ID
Returns:
DeviceEntity: device entity
"""
return (
db_session.query(DeviceEntity).filter(DeviceEntity.id == device_id).first()
)
@staticmethod
def store_access_log(db_session, card_number, device_id):
log = LogEntity(
device_id=device_id, card=card_number, datetime=datetime.utcnow()
)
db_session.add(log)
db_session.commit()
Classes
class Database (db_url=None)-
Database connection and SQLAlchemy wrapper
Expand source code
class Database: """Database connection and SQLAlchemy wrapper""" def __init__(self, db_url=None): if db_url is None: db_url = DB_CONNECTION_STRING self.engine = create_engine(db_url, poolclass=NullPool) self._create_tables() def _create_tables(self): Base.metadata.create_all(self.engine) def get_db_session(self): """Create database connection Returns: Database session, represents SQL transaction """ session_maker = sessionmaker(bind=self.engine) return session_maker() @staticmethod def select_questions(db_session, subjects, requested_number_of_questions): """Select questions for chose subjects Args: db_session: Database connection subjects (list): list with subjects requested_number_of_questions (int): number of requested questions Returns: list(QuestionEntity): questions """ questions = [] for subject in subjects: # select subject entity subject_entity = ( db_session.query(SubjectEntity) .filter(SubjectEntity.department_shortcut == subject["subject_code"]) .first() ) if subject_entity: number_of_questions = len(subject_entity.questions) if number_of_questions > 0: # select random question for subject index = random.choice(range(number_of_questions)) questions.append(subject_entity.questions[index]) questions_count = len(questions) if questions_count < requested_number_of_questions: # select default questions if there is less questions for chose subjects default_questions = ( db_session.query(QuestionEntity) .filter(QuestionEntity.department_shortcut == DEFAULT_SUBJECT_NAME) .order_by(func.random()) .limit(requested_number_of_questions - questions_count) ) questions.extend(default_questions) return questions @staticmethod def find_question_image(db_session, file_id): """Select picture by ID Args: db_session: Database connection file_id (int): ID of question Returns: QuestionEntity: entity with picture """ file = ( db_session.query(QuestionEntity.picture) .filter(QuestionEntity.id == file_id) .first() ) if not file: raise LookupError() return file @staticmethod def device_by_id(db_session, device_id): """Check if device with given id exists in DB Args: db_session: Database connection device_id(int): device ID Returns: DeviceEntity: device entity """ return ( db_session.query(DeviceEntity).filter(DeviceEntity.id == device_id).first() ) @staticmethod def store_access_log(db_session, card_number, device_id): log = LogEntity( device_id=device_id, card=card_number, datetime=datetime.utcnow() ) db_session.add(log) db_session.commit()Static methods
def device_by_id(db_session, device_id)-
Check if device with given id exists in DB
Args
db_session- Database connection
device_id(int): device ID
Returns
DeviceEntity- device entity
Expand source code
@staticmethod def device_by_id(db_session, device_id): """Check if device with given id exists in DB Args: db_session: Database connection device_id(int): device ID Returns: DeviceEntity: device entity """ return ( db_session.query(DeviceEntity).filter(DeviceEntity.id == device_id).first() ) def find_question_image(db_session, file_id)-
Select picture by ID
Args
db_session- Database connection
file_id:int- ID of question
Returns
QuestionEntity- entity with picture
Expand source code
@staticmethod def find_question_image(db_session, file_id): """Select picture by ID Args: db_session: Database connection file_id (int): ID of question Returns: QuestionEntity: entity with picture """ file = ( db_session.query(QuestionEntity.picture) .filter(QuestionEntity.id == file_id) .first() ) if not file: raise LookupError() return file def select_questions(db_session, subjects, requested_number_of_questions)-
Select questions for chose subjects
Args
db_session- Database connection
subjects:list- list with subjects
requested_number_of_questions:int- number of requested questions
Returns
list(QuestionEntity): questions
Expand source code
@staticmethod def select_questions(db_session, subjects, requested_number_of_questions): """Select questions for chose subjects Args: db_session: Database connection subjects (list): list with subjects requested_number_of_questions (int): number of requested questions Returns: list(QuestionEntity): questions """ questions = [] for subject in subjects: # select subject entity subject_entity = ( db_session.query(SubjectEntity) .filter(SubjectEntity.department_shortcut == subject["subject_code"]) .first() ) if subject_entity: number_of_questions = len(subject_entity.questions) if number_of_questions > 0: # select random question for subject index = random.choice(range(number_of_questions)) questions.append(subject_entity.questions[index]) questions_count = len(questions) if questions_count < requested_number_of_questions: # select default questions if there is less questions for chose subjects default_questions = ( db_session.query(QuestionEntity) .filter(QuestionEntity.department_shortcut == DEFAULT_SUBJECT_NAME) .order_by(func.random()) .limit(requested_number_of_questions - questions_count) ) questions.extend(default_questions) return questions def store_access_log(db_session, card_number, device_id)-
Expand source code
@staticmethod def store_access_log(db_session, card_number, device_id): log = LogEntity( device_id=device_id, card=card_number, datetime=datetime.utcnow() ) db_session.add(log) db_session.commit()
Methods
def get_db_session(self)-
Create database connection
Returns
Database session, represents SQL transaction
Expand source code
def get_db_session(self): """Create database connection Returns: Database session, represents SQL transaction """ session_maker = sessionmaker(bind=self.engine) return session_maker()
class DeviceEntity (**kwargs)-
Device database table definition
A simple constructor that allows initialization from kwargs.
Sets attributes on the constructed instance using the names and values in
kwargs.Only keys that are present as attributes of the instance's class are allowed. These could be, for example, any mapped columns or relationships.
Expand source code
class DeviceEntity(Base): """Device database table definition""" __tablename__ = "device" id = Column(Integer, primary_key=True) description = Column(String) logs = relationship("LogEntity", uselist=True, back_populates="device") study_programmes = relationship( "StudyProgrammeEntity", secondary=association_table, back_populates="devices" )Ancestors
- sqlalchemy.ext.declarative.api.Base
Instance variables
var description-
Expand source code
def __get__(self, instance, owner): if instance is None: return self dict_ = instance_dict(instance) if self._supports_population and self.key in dict_: return dict_[self.key] else: return self.impl.get(instance_state(instance), dict_) var id-
Expand source code
def __get__(self, instance, owner): if instance is None: return self dict_ = instance_dict(instance) if self._supports_population and self.key in dict_: return dict_[self.key] else: return self.impl.get(instance_state(instance), dict_) var logs-
Expand source code
def __get__(self, instance, owner): if instance is None: return self dict_ = instance_dict(instance) if self._supports_population and self.key in dict_: return dict_[self.key] else: return self.impl.get(instance_state(instance), dict_) var study_programmes-
Expand source code
def __get__(self, instance, owner): if instance is None: return self dict_ = instance_dict(instance) if self._supports_population and self.key in dict_: return dict_[self.key] else: return self.impl.get(instance_state(instance), dict_)
class LogEntity (**kwargs)-
Log database table definition
A simple constructor that allows initialization from kwargs.
Sets attributes on the constructed instance using the names and values in
kwargs.Only keys that are present as attributes of the instance's class are allowed. These could be, for example, any mapped columns or relationships.
Expand source code
class LogEntity(Base): """Log database table definition""" __tablename__ = "log" id = Column(Integer, primary_key=True) datetime = Column(DateTime, nullable=False) device_id = Column(Integer, ForeignKey("device.id"), nullable=False) card = Column(String, nullable=False) device = relationship("DeviceEntity", uselist=False, back_populates="logs")Ancestors
- sqlalchemy.ext.declarative.api.Base
Instance variables
var card-
Expand source code
def __get__(self, instance, owner): if instance is None: return self dict_ = instance_dict(instance) if self._supports_population and self.key in dict_: return dict_[self.key] else: return self.impl.get(instance_state(instance), dict_) var datetime-
Expand source code
def __get__(self, instance, owner): if instance is None: return self dict_ = instance_dict(instance) if self._supports_population and self.key in dict_: return dict_[self.key] else: return self.impl.get(instance_state(instance), dict_) var device-
Expand source code
def __get__(self, instance, owner): if instance is None: return self dict_ = instance_dict(instance) if self._supports_population and self.key in dict_: return dict_[self.key] else: return self.impl.get(instance_state(instance), dict_) var device_id-
Expand source code
def __get__(self, instance, owner): if instance is None: return self dict_ = instance_dict(instance) if self._supports_population and self.key in dict_: return dict_[self.key] else: return self.impl.get(instance_state(instance), dict_) var id-
Expand source code
def __get__(self, instance, owner): if instance is None: return self dict_ = instance_dict(instance) if self._supports_population and self.key in dict_: return dict_[self.key] else: return self.impl.get(instance_state(instance), dict_)
class QuestionEntity (**kwargs)-
Question database table definition
A simple constructor that allows initialization from kwargs.
Sets attributes on the constructed instance using the names and values in
kwargs.Only keys that are present as attributes of the instance's class are allowed. These could be, for example, any mapped columns or relationships.
Expand source code
class QuestionEntity(Base): """Question database table definition""" __tablename__ = "question" id = Column(Integer, primary_key=True) question = Column(String(255), nullable=False) department_shortcut = Column(String, ForeignKey("subject.department_shortcut")) right_answer = Column(String(255), nullable=False) wrong_answer_1 = Column(String(255), nullable=False) wrong_answer_2 = Column(String(255), nullable=False) wrong_answer_3 = Column(String(255), nullable=False) picture = Column(LargeBinary) subject = relationship("SubjectEntity", uselist=False, back_populates="questions")Ancestors
- sqlalchemy.ext.declarative.api.Base
Instance variables
var department_shortcut-
Expand source code
def __get__(self, instance, owner): if instance is None: return self dict_ = instance_dict(instance) if self._supports_population and self.key in dict_: return dict_[self.key] else: return self.impl.get(instance_state(instance), dict_) var id-
Expand source code
def __get__(self, instance, owner): if instance is None: return self dict_ = instance_dict(instance) if self._supports_population and self.key in dict_: return dict_[self.key] else: return self.impl.get(instance_state(instance), dict_) var picture-
Expand source code
def __get__(self, instance, owner): if instance is None: return self dict_ = instance_dict(instance) if self._supports_population and self.key in dict_: return dict_[self.key] else: return self.impl.get(instance_state(instance), dict_) var question-
Expand source code
def __get__(self, instance, owner): if instance is None: return self dict_ = instance_dict(instance) if self._supports_population and self.key in dict_: return dict_[self.key] else: return self.impl.get(instance_state(instance), dict_) var right_answer-
Expand source code
def __get__(self, instance, owner): if instance is None: return self dict_ = instance_dict(instance) if self._supports_population and self.key in dict_: return dict_[self.key] else: return self.impl.get(instance_state(instance), dict_) var subject-
Expand source code
def __get__(self, instance, owner): if instance is None: return self dict_ = instance_dict(instance) if self._supports_population and self.key in dict_: return dict_[self.key] else: return self.impl.get(instance_state(instance), dict_) var wrong_answer_1-
Expand source code
def __get__(self, instance, owner): if instance is None: return self dict_ = instance_dict(instance) if self._supports_population and self.key in dict_: return dict_[self.key] else: return self.impl.get(instance_state(instance), dict_) var wrong_answer_2-
Expand source code
def __get__(self, instance, owner): if instance is None: return self dict_ = instance_dict(instance) if self._supports_population and self.key in dict_: return dict_[self.key] else: return self.impl.get(instance_state(instance), dict_) var wrong_answer_3-
Expand source code
def __get__(self, instance, owner): if instance is None: return self dict_ = instance_dict(instance) if self._supports_population and self.key in dict_: return dict_[self.key] else: return self.impl.get(instance_state(instance), dict_)
class StudyProgrammeEntity (**kwargs)-
Study programme table definition
A simple constructor that allows initialization from kwargs.
Sets attributes on the constructed instance using the names and values in
kwargs.Only keys that are present as attributes of the instance's class are allowed. These could be, for example, any mapped columns or relationships.
Expand source code
class StudyProgrammeEntity(Base): """Study programme table definition""" __tablename__ = "study_programme" id = Column(Integer, primary_key=True) study_code = Column(String, nullable=False) description = Column(String) devices = relationship( "DeviceEntity", secondary=association_table, back_populates="study_programmes" )Ancestors
- sqlalchemy.ext.declarative.api.Base
Instance variables
var description-
Expand source code
def __get__(self, instance, owner): if instance is None: return self dict_ = instance_dict(instance) if self._supports_population and self.key in dict_: return dict_[self.key] else: return self.impl.get(instance_state(instance), dict_) var devices-
Expand source code
def __get__(self, instance, owner): if instance is None: return self dict_ = instance_dict(instance) if self._supports_population and self.key in dict_: return dict_[self.key] else: return self.impl.get(instance_state(instance), dict_) var id-
Expand source code
def __get__(self, instance, owner): if instance is None: return self dict_ = instance_dict(instance) if self._supports_population and self.key in dict_: return dict_[self.key] else: return self.impl.get(instance_state(instance), dict_) var study_code-
Expand source code
def __get__(self, instance, owner): if instance is None: return self dict_ = instance_dict(instance) if self._supports_population and self.key in dict_: return dict_[self.key] else: return self.impl.get(instance_state(instance), dict_)
class SubjectEntity (**kwargs)-
Subject database table definition
A simple constructor that allows initialization from kwargs.
Sets attributes on the constructed instance using the names and values in
kwargs.Only keys that are present as attributes of the instance's class are allowed. These could be, for example, any mapped columns or relationships.
Expand source code
class SubjectEntity(Base): """Subject database table definition""" __tablename__ = "subject" department_shortcut = Column(String, primary_key=True) name = Column(String(30)) # 1 (subject): M(questions) relationship questions = relationship("QuestionEntity", uselist=True, back_populates="subject")Ancestors
- sqlalchemy.ext.declarative.api.Base
Instance variables
var department_shortcut-
Expand source code
def __get__(self, instance, owner): if instance is None: return self dict_ = instance_dict(instance) if self._supports_population and self.key in dict_: return dict_[self.key] else: return self.impl.get(instance_state(instance), dict_) var name-
Expand source code
def __get__(self, instance, owner): if instance is None: return self dict_ = instance_dict(instance) if self._supports_population and self.key in dict_: return dict_[self.key] else: return self.impl.get(instance_state(instance), dict_) var questions-
Expand source code
def __get__(self, instance, owner): if instance is None: return self dict_ = instance_dict(instance) if self._supports_population and self.key in dict_: return dict_[self.key] else: return self.impl.get(instance_state(instance), dict_)